#ifndef __SDL_EVENT_HPP
#define __SDL_EVENT_HPP

#include "sdldefs.h"

#include "SDL.h"

#include <set>

class sdlVoidClass;
typedef void (sdlVoidClass::*sdlVOIDFNC)(SDL_Event&);
#define MemberFunction (void (sdlVoidClass::*)(SDL_Event&))

// void class used in assigning member functions to events
class sdlVoidClass
{
public:
    sdlVOIDFNC fnc;
};

// ties a command to a function
struct sdlEvtCommand
{
    sdlEventType type;
    sdlVOIDFNC   function;

    bool sdlEvtCommand::operator< (const sdlEvtCommand &o) const
    {
        return type < o.type;
    }

    bool sdlEvtCommand::operator== (const sdlEvtCommand &o) const
    {
        return type == o.type;
    }
};

// this class is used in all event handling
class sdlEvent
{
protected:
    void * parentPtr;
    std::set<sdlEvtCommand> commandSet;
    static void __stdcall callFunctionInternal(void * classPtr, sdlVOIDFNC function, SDL_Event &e)
    {
        //FIXIT Fix the compiler checks
        #ifdef _MSC_VER
        __asm
        {
            mov  ecx,[ebp+0x08]       // this *
            mov  eax,[ebp+0x0C]       // function *
            push dword ptr [ebp+0x10] //SDL_Event
            call eax
        }
        #else
        // don't make stack frame for GCC.  GCC ignores naked attribute.
        asm __volatile__(".intel_syntax noprefix\n"
            "mov ecx,[ebp+0x08]\n"
            "mov eax,[ebp+0x0C]\n"
            "push dword ptr [ebp+0x10]\n"
            "push ecx\n" // GCC's thiscall is different than MSVC
            "call eax\n"
            ".att_syntax\n");
        //asm __volatile__("call $getParent");
        //asm __volatile__("movl %eax %ecx");
        //asm __volatile__("movl (iter) %eax");
        //asm __volatile__("addl function %eax");
        //asm __volatile__("movl (%eax) %eax");
        //asm __volatile__("push _e");
        //asm __volatile__("call %eax");
        #endif
    }

public:
    sdlEvent(void * parent = NULL)
    {
        parentPtr = parent;
    }

    sdlEvent(const sdlEvent& o)
    {
        parentPtr  = o.parentPtr;
        commandSet = o.commandSet;
    }

    sdlEvent& operator=( const sdlEvent& o)
    {
        if( this != &o )
        {
            parentPtr  = o.parentPtr;
            commandSet = o.commandSet;
        }
        return *this;
    }

    virtual ~sdlEvent()
    {
        ;
    }

    void callFunction(const sdlEventType type, SDL_Event &e)
    {
        sdlEvtCommand tempCommand;
        tempCommand.type = type;
        std::set<sdlEvtCommand>::iterator iter = commandSet.find(tempCommand);
        if( iter != commandSet.end() ) // we found the command
        {
            callFunctionInternal(parentPtr, iter->function, e);
        }
    }

    void connect( const sdlEventType e, sdlVOIDFNC function )
    {
        sdlEvtCommand evtCommand;
        evtCommand.function = function;
        evtCommand.type     = e;
        commandSet.insert(evtCommand);
    }

    void * getParent()
    {
        return parentPtr;
    }

    virtual void update( SDL_Event &e )
    {
        ;
    }
};

#endif //SDL_EVENT_HPP
